#pragma once
#include <pthread.h>
#include <vector>
#include <queue>
#include <errno.h>
#include "LogMessage.hpp"

const size_t THREAD_NUM = 6;

template <class Task>
class ThreadPool
{
public:
    ThreadPool(size_t threadNum = THREAD_NUM)
        : _threadNum(threadNum),
        _stop(true)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    void Run()
    {
        _stop = false;
        for (int i = 0; i < _threadNum; ++i)
        {
            pthread_t tid;
            int ret = pthread_create(&tid, nullptr, ThreadRoutine, this);
            if (ret != 0)
            {
                LogMessage(FATAL, "pthread_create fatal! code:%d desc:%s", errno, strerror(errno));
                exit(errno);
            }
            _threads.push_back(tid);
            LogMessage(TEST, "pthread_create tid:%ul", tid);
        }
    }

    ~ThreadPool()
    {
        _stop = true;
        pthread_cond_broadcast(&_cond);
        for (int i = 0; i < _threads.size(); ++i)
        {
            pthread_join(_threads[i], nullptr);
            LogMessage(TEST, "pthread_join tid:%ul", _threads[i]);
        }
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cond);
    }

    void PushTask(const Task &task)
    {
        pthread_mutex_lock(&_mtx);
        _taskQueue.push(task);
        pthread_cond_signal(&_cond);
        pthread_mutex_unlock(&_mtx);
    }

    Task PopTask()
    {
        pthread_mutex_lock(&_mtx);
        while (_taskQueue.empty())
        {
            pthread_cond_wait(&_cond, &_mtx);
            if (_stop)
            {
                pthread_mutex_unlock(&_mtx);
                pthread_exit(nullptr);
            }
        }
        Task task = _taskQueue.front();
        _taskQueue.pop();
        pthread_mutex_unlock(&_mtx);
        return task;
    }

    static void *ThreadRoutine(void *arg)
    {
        ThreadPool *tp = (ThreadPool *)arg;
        while (!tp->_stop)
        {
            Task task = tp->PopTask();
            task();
        }
        return nullptr;
    }

private:
    std::vector<pthread_t> _threads;
    std::queue<Task> _taskQueue;
    size_t _threadNum;
    bool _stop;
    pthread_mutex_t _mtx;
    pthread_cond_t _cond;
};